Hlasování

Nastavení prostředí

In [1]:
if 'google.colab' in str(get_ipython()):
        import os, sys
        os.chdir('/content')
        print('Nastavení prostředí Google Colab.')
        ! git clone "https://github.com/jarbes/snemovna.git" --branch master
        sys.path.insert(0,'/content/snemovna')
        os.chdir('/content/snemovna')
In [2]:
! pip install -r requirements.txt
Requirement already satisfied: bs4 in ./venv/lib/python3.8/site-packages (from -r requirements.txt (line 1)) (0.0.1)
Requirement already satisfied: dash in ./venv/lib/python3.8/site-packages (from -r requirements.txt (line 2)) (1.19.0)
Requirement already satisfied: html2text in ./venv/lib/python3.8/site-packages (from -r requirements.txt (line 3)) (2020.1.16)
Requirement already satisfied: html5lib in ./venv/lib/python3.8/site-packages (from -r requirements.txt (line 4)) (1.1)
Requirement already satisfied: jupyterlab in ./venv/lib/python3.8/site-packages (from -r requirements.txt (line 5)) (3.0.11)
Requirement already satisfied: matplotlib in ./venv/lib/python3.8/site-packages (from -r requirements.txt (line 6)) (3.3.4)
Requirement already satisfied: numpy>=1.18.4 in ./venv/lib/python3.8/site-packages (from -r requirements.txt (line 7)) (1.20.1)
Requirement already satisfied: pandas==1.1.5 in ./venv/lib/python3.8/site-packages (from -r requirements.txt (line 8)) (1.1.5)
Requirement already satisfied: plotly in ./venv/lib/python3.8/site-packages (from -r requirements.txt (line 9)) (4.14.3)
Requirement already satisfied: requests in ./venv/lib/python3.8/site-packages (from -r requirements.txt (line 10)) (2.25.1)
Requirement already satisfied: joblib in ./venv/lib/python3.8/site-packages (from -r requirements.txt (line 11)) (1.0.1)
Requirement already satisfied: beautifulsoup4 in ./venv/lib/python3.8/site-packages (from bs4->-r requirements.txt (line 1)) (4.9.3)
Requirement already satisfied: dash-renderer==1.9.0 in ./venv/lib/python3.8/site-packages (from dash->-r requirements.txt (line 2)) (1.9.0)
Requirement already satisfied: dash-html-components==1.1.2 in ./venv/lib/python3.8/site-packages (from dash->-r requirements.txt (line 2)) (1.1.2)
Requirement already satisfied: flask-compress in ./venv/lib/python3.8/site-packages (from dash->-r requirements.txt (line 2)) (1.9.0)
Requirement already satisfied: Flask>=1.0.4 in ./venv/lib/python3.8/site-packages (from dash->-r requirements.txt (line 2)) (1.1.2)
Requirement already satisfied: dash-core-components==1.15.0 in ./venv/lib/python3.8/site-packages (from dash->-r requirements.txt (line 2)) (1.15.0)
Requirement already satisfied: future in ./venv/lib/python3.8/site-packages (from dash->-r requirements.txt (line 2)) (0.18.2)
Requirement already satisfied: dash-table==4.11.2 in ./venv/lib/python3.8/site-packages (from dash->-r requirements.txt (line 2)) (4.11.2)
Requirement already satisfied: webencodings in ./venv/lib/python3.8/site-packages (from html5lib->-r requirements.txt (line 4)) (0.5.1)
Requirement already satisfied: six>=1.9 in ./venv/lib/python3.8/site-packages (from html5lib->-r requirements.txt (line 4)) (1.15.0)
Requirement already satisfied: jinja2>=2.10 in ./venv/lib/python3.8/site-packages (from jupyterlab->-r requirements.txt (line 5)) (2.11.3)
Requirement already satisfied: jupyter-server~=1.4 in ./venv/lib/python3.8/site-packages (from jupyterlab->-r requirements.txt (line 5)) (1.4.1)
Requirement already satisfied: jupyter-core in ./venv/lib/python3.8/site-packages (from jupyterlab->-r requirements.txt (line 5)) (4.7.1)
Requirement already satisfied: jupyter-packaging~=0.7.3 in ./venv/lib/python3.8/site-packages (from jupyterlab->-r requirements.txt (line 5)) (0.7.12)
Requirement already satisfied: tornado>=6.1.0 in ./venv/lib/python3.8/site-packages (from jupyterlab->-r requirements.txt (line 5)) (6.1)
Requirement already satisfied: packaging in ./venv/lib/python3.8/site-packages (from jupyterlab->-r requirements.txt (line 5)) (20.9)
Requirement already satisfied: jupyterlab-server~=2.3 in ./venv/lib/python3.8/site-packages (from jupyterlab->-r requirements.txt (line 5)) (2.3.0)
Requirement already satisfied: nbclassic~=0.2 in ./venv/lib/python3.8/site-packages (from jupyterlab->-r requirements.txt (line 5)) (0.2.6)
Requirement already satisfied: ipython in ./venv/lib/python3.8/site-packages (from jupyterlab->-r requirements.txt (line 5)) (7.21.0)
Requirement already satisfied: python-dateutil>=2.1 in ./venv/lib/python3.8/site-packages (from matplotlib->-r requirements.txt (line 6)) (2.8.1)
Requirement already satisfied: pillow>=6.2.0 in ./venv/lib/python3.8/site-packages (from matplotlib->-r requirements.txt (line 6)) (8.1.2)
Requirement already satisfied: pyparsing!=2.0.4,!=2.1.2,!=2.1.6,>=2.0.3 in ./venv/lib/python3.8/site-packages (from matplotlib->-r requirements.txt (line 6)) (2.4.7)
Requirement already satisfied: cycler>=0.10 in ./venv/lib/python3.8/site-packages (from matplotlib->-r requirements.txt (line 6)) (0.10.0)
Requirement already satisfied: kiwisolver>=1.0.1 in ./venv/lib/python3.8/site-packages (from matplotlib->-r requirements.txt (line 6)) (1.3.1)
Requirement already satisfied: pytz>=2017.2 in ./venv/lib/python3.8/site-packages (from pandas==1.1.5->-r requirements.txt (line 8)) (2021.1)
Requirement already satisfied: retrying>=1.3.3 in ./venv/lib/python3.8/site-packages (from plotly->-r requirements.txt (line 9)) (1.3.3)
Requirement already satisfied: chardet<5,>=3.0.2 in ./venv/lib/python3.8/site-packages (from requests->-r requirements.txt (line 10)) (4.0.0)
Requirement already satisfied: idna<3,>=2.5 in ./venv/lib/python3.8/site-packages (from requests->-r requirements.txt (line 10)) (2.10)
Requirement already satisfied: urllib3<1.27,>=1.21.1 in ./venv/lib/python3.8/site-packages (from requests->-r requirements.txt (line 10)) (1.26.4)
Requirement already satisfied: certifi>=2017.4.17 in ./venv/lib/python3.8/site-packages (from requests->-r requirements.txt (line 10)) (2020.12.5)
Requirement already satisfied: soupsieve>1.2; python_version >= "3.0" in ./venv/lib/python3.8/site-packages (from beautifulsoup4->bs4->-r requirements.txt (line 1)) (2.2)
Requirement already satisfied: brotli in ./venv/lib/python3.8/site-packages (from flask-compress->dash->-r requirements.txt (line 2)) (1.0.9)
Requirement already satisfied: itsdangerous>=0.24 in ./venv/lib/python3.8/site-packages (from Flask>=1.0.4->dash->-r requirements.txt (line 2)) (1.1.0)
Requirement already satisfied: Werkzeug>=0.15 in ./venv/lib/python3.8/site-packages (from Flask>=1.0.4->dash->-r requirements.txt (line 2)) (1.0.1)
Requirement already satisfied: click>=5.1 in ./venv/lib/python3.8/site-packages (from Flask>=1.0.4->dash->-r requirements.txt (line 2)) (7.1.2)
Requirement already satisfied: MarkupSafe>=0.23 in ./venv/lib/python3.8/site-packages (from jinja2>=2.10->jupyterlab->-r requirements.txt (line 5)) (1.1.1)
Requirement already satisfied: anyio>=2.0.2 in ./venv/lib/python3.8/site-packages (from jupyter-server~=1.4->jupyterlab->-r requirements.txt (line 5)) (2.2.0)
Requirement already satisfied: nbformat in ./venv/lib/python3.8/site-packages (from jupyter-server~=1.4->jupyterlab->-r requirements.txt (line 5)) (5.1.2)
Requirement already satisfied: nbconvert in ./venv/lib/python3.8/site-packages (from jupyter-server~=1.4->jupyterlab->-r requirements.txt (line 5)) (6.0.7)
Requirement already satisfied: ipython-genutils in ./venv/lib/python3.8/site-packages (from jupyter-server~=1.4->jupyterlab->-r requirements.txt (line 5)) (0.2.0)
Requirement already satisfied: Send2Trash in ./venv/lib/python3.8/site-packages (from jupyter-server~=1.4->jupyterlab->-r requirements.txt (line 5)) (1.5.0)
Requirement already satisfied: terminado>=0.8.3 in ./venv/lib/python3.8/site-packages (from jupyter-server~=1.4->jupyterlab->-r requirements.txt (line 5)) (0.9.3)
Requirement already satisfied: pyzmq>=17 in ./venv/lib/python3.8/site-packages (from jupyter-server~=1.4->jupyterlab->-r requirements.txt (line 5)) (22.0.3)
Requirement already satisfied: prometheus-client in ./venv/lib/python3.8/site-packages (from jupyter-server~=1.4->jupyterlab->-r requirements.txt (line 5)) (0.9.0)
Requirement already satisfied: traitlets>=4.2.1 in ./venv/lib/python3.8/site-packages (from jupyter-server~=1.4->jupyterlab->-r requirements.txt (line 5)) (5.0.5)
Requirement already satisfied: jupyter-client>=6.1.1 in ./venv/lib/python3.8/site-packages (from jupyter-server~=1.4->jupyterlab->-r requirements.txt (line 5)) (6.1.12)
Requirement already satisfied: jsonschema>=3.0.1 in ./venv/lib/python3.8/site-packages (from jupyterlab-server~=2.3->jupyterlab->-r requirements.txt (line 5)) (3.2.0)
Requirement already satisfied: babel in ./venv/lib/python3.8/site-packages (from jupyterlab-server~=2.3->jupyterlab->-r requirements.txt (line 5)) (2.9.0)
Requirement already satisfied: json5 in ./venv/lib/python3.8/site-packages (from jupyterlab-server~=2.3->jupyterlab->-r requirements.txt (line 5)) (0.9.5)
Requirement already satisfied: notebook<7 in ./venv/lib/python3.8/site-packages (from nbclassic~=0.2->jupyterlab->-r requirements.txt (line 5)) (6.2.0)
Requirement already satisfied: pygments in ./venv/lib/python3.8/site-packages (from ipython->jupyterlab->-r requirements.txt (line 5)) (2.8.1)
Requirement already satisfied: pickleshare in ./venv/lib/python3.8/site-packages (from ipython->jupyterlab->-r requirements.txt (line 5)) (0.7.5)
Requirement already satisfied: prompt-toolkit!=3.0.0,!=3.0.1,<3.1.0,>=2.0.0 in ./venv/lib/python3.8/site-packages (from ipython->jupyterlab->-r requirements.txt (line 5)) (3.0.17)
Requirement already satisfied: jedi>=0.16 in ./venv/lib/python3.8/site-packages (from ipython->jupyterlab->-r requirements.txt (line 5)) (0.18.0)
Requirement already satisfied: decorator in ./venv/lib/python3.8/site-packages (from ipython->jupyterlab->-r requirements.txt (line 5)) (4.4.2)
Requirement already satisfied: backcall in ./venv/lib/python3.8/site-packages (from ipython->jupyterlab->-r requirements.txt (line 5)) (0.2.0)
Requirement already satisfied: setuptools>=18.5 in ./venv/lib/python3.8/site-packages (from ipython->jupyterlab->-r requirements.txt (line 5)) (44.0.0)
Requirement already satisfied: pexpect>4.3; sys_platform != "win32" in ./venv/lib/python3.8/site-packages (from ipython->jupyterlab->-r requirements.txt (line 5)) (4.8.0)
Requirement already satisfied: sniffio>=1.1 in ./venv/lib/python3.8/site-packages (from anyio>=2.0.2->jupyter-server~=1.4->jupyterlab->-r requirements.txt (line 5)) (1.2.0)
Requirement already satisfied: pandocfilters>=1.4.1 in ./venv/lib/python3.8/site-packages (from nbconvert->jupyter-server~=1.4->jupyterlab->-r requirements.txt (line 5)) (1.4.3)
Requirement already satisfied: jupyterlab-pygments in ./venv/lib/python3.8/site-packages (from nbconvert->jupyter-server~=1.4->jupyterlab->-r requirements.txt (line 5)) (0.1.2)
Requirement already satisfied: entrypoints>=0.2.2 in ./venv/lib/python3.8/site-packages (from nbconvert->jupyter-server~=1.4->jupyterlab->-r requirements.txt (line 5)) (0.3)
Requirement already satisfied: testpath in ./venv/lib/python3.8/site-packages (from nbconvert->jupyter-server~=1.4->jupyterlab->-r requirements.txt (line 5)) (0.4.4)
Requirement already satisfied: defusedxml in ./venv/lib/python3.8/site-packages (from nbconvert->jupyter-server~=1.4->jupyterlab->-r requirements.txt (line 5)) (0.7.1)
Requirement already satisfied: nbclient<0.6.0,>=0.5.0 in ./venv/lib/python3.8/site-packages (from nbconvert->jupyter-server~=1.4->jupyterlab->-r requirements.txt (line 5)) (0.5.3)
Requirement already satisfied: mistune<2,>=0.8.1 in ./venv/lib/python3.8/site-packages (from nbconvert->jupyter-server~=1.4->jupyterlab->-r requirements.txt (line 5)) (0.8.4)
Requirement already satisfied: bleach in ./venv/lib/python3.8/site-packages (from nbconvert->jupyter-server~=1.4->jupyterlab->-r requirements.txt (line 5)) (3.3.0)
Requirement already satisfied: ptyprocess; os_name != "nt" in ./venv/lib/python3.8/site-packages (from terminado>=0.8.3->jupyter-server~=1.4->jupyterlab->-r requirements.txt (line 5)) (0.7.0)
Requirement already satisfied: pyrsistent>=0.14.0 in ./venv/lib/python3.8/site-packages (from jsonschema>=3.0.1->jupyterlab-server~=2.3->jupyterlab->-r requirements.txt (line 5)) (0.17.3)
Requirement already satisfied: attrs>=17.4.0 in ./venv/lib/python3.8/site-packages (from jsonschema>=3.0.1->jupyterlab-server~=2.3->jupyterlab->-r requirements.txt (line 5)) (20.3.0)
Requirement already satisfied: ipykernel in ./venv/lib/python3.8/site-packages (from notebook<7->nbclassic~=0.2->jupyterlab->-r requirements.txt (line 5)) (5.5.0)
Requirement already satisfied: argon2-cffi in ./venv/lib/python3.8/site-packages (from notebook<7->nbclassic~=0.2->jupyterlab->-r requirements.txt (line 5)) (20.1.0)
Requirement already satisfied: wcwidth in ./venv/lib/python3.8/site-packages (from prompt-toolkit!=3.0.0,!=3.0.1,<3.1.0,>=2.0.0->ipython->jupyterlab->-r requirements.txt (line 5)) (0.2.5)
Requirement already satisfied: parso<0.9.0,>=0.8.0 in ./venv/lib/python3.8/site-packages (from jedi>=0.16->ipython->jupyterlab->-r requirements.txt (line 5)) (0.8.1)
Requirement already satisfied: async-generator in ./venv/lib/python3.8/site-packages (from nbclient<0.6.0,>=0.5.0->nbconvert->jupyter-server~=1.4->jupyterlab->-r requirements.txt (line 5)) (1.10)
Requirement already satisfied: nest-asyncio in ./venv/lib/python3.8/site-packages (from nbclient<0.6.0,>=0.5.0->nbconvert->jupyter-server~=1.4->jupyterlab->-r requirements.txt (line 5)) (1.5.1)
Requirement already satisfied: cffi>=1.0.0 in ./venv/lib/python3.8/site-packages (from argon2-cffi->notebook<7->nbclassic~=0.2->jupyterlab->-r requirements.txt (line 5)) (1.14.5)
Requirement already satisfied: pycparser in ./venv/lib/python3.8/site-packages (from cffi>=1.0.0->argon2-cffi->notebook<7->nbclassic~=0.2->jupyterlab->-r requirements.txt (line 5)) (2.20)

Import knihoven

In [3]:
from datetime import datetime, timedelta

import plotly.express as px
import plotly.graph_objects as go

import pandas as pd
import numpy as np
In [4]:
from snemovna.Hlasovani import Hlasovani, Omluvy, HlasovaniPoslanci, ZpochybneniHlasovani, ZmatecneHlasovani
from snemovna.PoslanciOsoby import Poslanci, Organy, ZarazeniOsoby
from snemovna.Stenotexty import Stenotexty
from snemovna.Schuze import Schuze
from snemovna.utility import groupby_bar, sort_column_by_predefined_order
from nastav_jupyter_notebook import nastav_pandas
In [5]:
# nastavení výpisu, například zobrazení delších textů v sloupcích tabulek
nastav_pandas()

Nastavení

In [6]:
clean_layout = dict(
    plot_bgcolor="#FFFFFF",
)

y_spikes = dict(
    yaxis=dict(
        linecolor="#BCCCDC",
        showspikes=True,
        spikethickness=1,
        spikedash="dot",
        spikecolor="#999999",
        spikemode="across",

    )
)

x_spikes = dict(
    xaxis=dict(
        linecolor="#BCCCDC",
        showspikes=True,
        spikethickness=1,
        spikedash="dot",
        spikecolor="#999999",
        spikemode="across",

    )
)

clean_layout_with_x_spikes = {**clean_layout, **x_spikes}
clean_layout_with_y_spikes = {**clean_layout, **y_spikes}
clean_layout_with_xy_spikes = {**clean_layout, **x_spikes, **y_spikes}

categorical_scale1 = ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd', '#8c564b', '#e377c2', '#7f7f7f', '#bcbd22', '#17becf']
In [7]:
# formát výpisu data
format_den = "%d. %m. %Y"

Načtení tabulek

In [8]:
stahni=True
In [9]:
# načti informace o poslancích (jméno, příjemní, za koho kandidovali, etc.)
p = Poslanci(stahni=stahni)
print(p.columns)
p.head(2)
2021-03-18:12:31:41 INFO     [utility.py:21] Stahuji 'https://www.psp.cz/eknih/cdrom/opendata/poslanci.zip'.
2021-03-18:12:31:43 WARNING  [Snemovna.py:149] While merging 'funkce' with 'typ_funkce': Dropping ['nazev_typ_organ_cz__typ_funkce', 'id_typ_organ__typ_funkce', 'typ_id_typ_organ__typ_funkce', 'nazev_typ_organ_en__typ_funkce', 'typ_organ_obecny__typ_funkce'] because of abundance.
Index(['id_poslanec', 'id_osoba', 'id_kraj', 'id_kandidatka', 'id_organ',
       'web', 'ulice', 'obec', 'psc', 'email', 'telefon', 'fax', 'psp_telefon',
       'facebook', 'foto', 'pred', 'prijmeni', 'jmeno', 'za', 'narozeni',
       'zmena', 'umrti', 'pohlavi', 'adresa', 'sirka', 'delka',
       'nazev_kandidatka_cz', 'zkratka_kandidatka', 'nazev_kraj_cz',
       'zkratka_kraj', 'id_parlament', 'od_parlament', 'do_parlament',
       'id_klub', 'nazev_klub_cz', 'zkratka_klub', 'od_klub', 'do_klub'],
      dtype='object')
Out[9]:
id_poslanec id_osoba id_kraj id_kandidatka id_organ web ulice obec psc email ... nazev_kraj_cz zkratka_kraj id_parlament od_parlament do_parlament id_klub nazev_klub_cz zkratka_klub od_klub do_klub
index
0 1521 5700 588 155 172 <NA> Vančurovo nám. 663 Hradec Králové 50002 adameci@psp.cz ... Královéhradecký HK 172 2017-10-21 14:00:00+02:00 NaT 1295 Poslanecký klub Občanské demokratické strany ODS 2017-10-24 00:00:00+02:00 NaT
1 1522 6254 581 1104 172 <NA> Dobronická 1257 Praha 4 - Kunratice 14800 adamkovav@psp.cz ... Hlavní město Praha PH 172 2017-10-21 14:00:00+02:00 NaT 1292 Poslanecký klub ANO 2011 ANO 2017-10-24 00:00:00+02:00 NaT

2 rows × 38 columns

In [10]:
zo = ZarazeniOsoby(stahni=stahni)
print(zo.columns)
zo.head(2)
2021-03-18:12:31:44 INFO     [utility.py:21] Stahuji 'https://www.psp.cz/eknih/cdrom/opendata/poslanci.zip'.
2021-03-18:12:31:46 WARNING  [Snemovna.py:149] While merging 'funkce' with 'typ_funkce': Dropping ['nazev_typ_organ_cz__typ_funkce', 'id_typ_organ__typ_funkce', 'typ_id_typ_organ__typ_funkce', 'nazev_typ_organ_en__typ_funkce', 'typ_organ_obecny__typ_funkce'] because of abundance.
Index(['id_osoba', 'id_of', 'cl_funkce__ORIG', 'od_o', 'do_o', 'od_f', 'do_f',
       'cl_funkce', 'pred', 'prijmeni', 'jmeno', 'za', 'narozeni',
       'pohlavi__ORIG', 'zmena', 'umrti', 'pohlavi', 'id_organ',
       'organ_id_organ', 'id_typ_organ', 'zkratka', 'nazev_organ_cz',
       'nazev_organ_en', 'od_organ', 'do_organ', 'cl_organ_base',
       'typ_id_typ_organ', 'nazev_typ_organ_cz', 'nazev_typ_organ_en',
       'typ_organ_obecny', 'id_funkce', 'id_typ_funkce', 'nazev_funkce_cz',
       'priorita', 'typ_funkce_cz', 'typ_funkce_en', 'typ_funkce_obecny__ORIG',
       'typ_funkce_obecny'],
      dtype='object')
Out[10]:
id_osoba id_of cl_funkce__ORIG od_o do_o od_f do_f cl_funkce pred prijmeni ... nazev_typ_organ_en typ_organ_obecny id_funkce id_typ_funkce nazev_funkce_cz priorita typ_funkce_cz typ_funkce_en typ_funkce_obecny__ORIG typ_funkce_obecny
index
38 4 172 0 2017-10-21 14:00:00+02:00 NaT NaT NaT členství Benda ... Parliament <NA> <NA> <NA> <NA> <NA> <NA> <NA> <NA> <NA>
95 4 1295 0 2017-10-24 00:00:00+02:00 NaT NaT NaT členství Benda ... Political Group 1 <NA> <NA> <NA> <NA> <NA> <NA> <NA> <NA>

2 rows × 38 columns

In [11]:
# načti informace o schůzích PS (kdy začaly, v jakém jsou stavu, etc.)
sch = Schuze(stahni=stahni)
print(sch.columns)
sch.head(2)
2021-03-18:12:31:46 INFO     [utility.py:21] Stahuji 'https://www.psp.cz/eknih/cdrom/opendata/poslanci.zip'.
2021-03-18:12:31:48 INFO     [utility.py:21] Stahuji 'https://www.psp.cz/eknih/cdrom/opendata/schuze.zip'.
Index(['id_schuze', 'id_org', 'schuze', 'od_schuze', 'do_schuze',
       'aktualizace', 'pozvanka', 'text_dt', 'text_st', 'tm_line', 'stav',
       'typ'],
      dtype='object')
Out[11]:
id_schuze id_org schuze od_schuze do_schuze aktualizace pozvanka text_dt text_st tm_line stav typ
0 469 172 1 2017-11-20 13:00:00+01:00 NaT 2017-11-08 08:58 navržený pořad <NA> <NA> <NA> OK řádná
1 469 172 1 2017-11-20 13:00:00+01:00 2017-11-24 14:09:00+01:00 2017-11-24 14:09 <NA> <NA> <NA> <NA> OK řádná
In [12]:
# načti informace o jednotlivých hlasováních
h = Hlasovani(stahni=stahni)
print(h.columns)
h.head(2)
2021-03-18:12:31:50 INFO     [utility.py:21] Stahuji 'https://www.psp.cz/eknih/cdrom/opendata/poslanci.zip'.
2021-03-18:12:31:51 INFO     [utility.py:21] Stahuji 'https://www.psp.cz/eknih/cdrom/opendata/hl-2017ps.zip'.
Index(['id_hlasovani', 'id_organ', 'schuze', 'cislo', 'bod', 'cas', 'pro',
       'proti', 'zdrzel', 'nehlasoval', 'prihlaseno', 'kvorum', 'nazev_dlouhy',
       'nazev_kratky', 'datum', 'bod__KAT', 'vysledek', 'druh_hlasovani',
       'ma_zpochybneni', 'je_zmatecne', 'ma_stenozaznam', 'turn', 'typ'],
      dtype='object')
Out[12]:
id_hlasovani id_organ schuze cislo bod cas pro proti zdrzel nehlasoval ... nazev_kratky datum bod__KAT vysledek druh_hlasovani ma_zpochybneni je_zmatecne ma_stenozaznam turn typ
0 67018 172 1 1 3 13:53:00 191 0 5 0 ... <NA> 2017-11-20 13:53:00+01:00 normální přijato normální False True False <NA> <NA>
1 67019 172 1 2 3 13:53:00 194 0 4 0 ... <NA> 2017-11-20 13:53:00+01:00 normální přijato normální False False False <NA> <NA>

2 rows × 23 columns

In [13]:
zph = ZpochybneniHlasovani(stahni=stahni)
print(zph.columns)
zph.head()
2021-03-18:12:31:55 INFO     [utility.py:21] Stahuji 'https://www.psp.cz/eknih/cdrom/opendata/poslanci.zip'.
2021-03-18:12:31:56 INFO     [utility.py:21] Stahuji 'https://www.psp.cz/eknih/cdrom/opendata/hl-2017ps.zip'.
2021-03-18:12:32:00 WARNING  [Snemovna.py:149] While merging 'zpochybneni' with 'hlasovani': Dropping ['turn__hlasovani'] because of abundance.
2021-03-18:12:32:00 WARNING  [Snemovna.py:184] Pro sloupec 'je_platne' nebyla nalezena metadata!
Index(['id_hlasovani', 'turn', 'mode', 'id_h2', 'id_h3', 'mode__KAT',
       'je_platne', 'id_organ', 'schuze', 'cislo', 'bod', 'datum__ORIG', 'cas',
       'pro', 'proti', 'zdrzel', 'nehlasoval', 'prihlaseno', 'kvorum',
       'druh_hlasovani__ORIG', 'vysledek__ORIG', 'nazev_dlouhy',
       'nazev_kratky', 'datum', 'bod__KAT', 'vysledek', 'druh_hlasovani',
       'ma_zpochybneni', 'je_zmatecne', 'ma_stenozaznam', 'typ__ORIG', 'typ'],
      dtype='object')
Out[13]:
id_hlasovani turn mode id_h2 id_h3 mode__KAT je_platne id_organ schuze cislo ... nazev_kratky datum bod__KAT vysledek druh_hlasovani ma_zpochybneni je_zmatecne ma_stenozaznam typ__ORIG typ
0 55680 67 0 55681 55682 žádost o opakování True <NA> <NA> <NA> ... <NA> NaT <NA> <NA> <NA> NaN NaN NaN <NA> <NA>
1 55664 43 0 55665 55666 žádost o opakování True <NA> <NA> <NA> ... <NA> NaT <NA> <NA> <NA> NaN NaN NaN <NA> <NA>
2 55590 244 0 55591 55592 žádost o opakování True <NA> <NA> <NA> ... <NA> NaT <NA> <NA> <NA> NaN NaN NaN <NA> <NA>
3 55561 200 0 55562 55563 žádost o opakování True <NA> <NA> <NA> ... <NA> NaT <NA> <NA> <NA> NaN NaN NaN <NA> <NA>
4 55555 199 0 55556 55557 žádost o opakování True <NA> <NA> <NA> ... <NA> NaT <NA> <NA> <NA> NaN NaN NaN <NA> <NA>

5 rows × 32 columns

In [14]:
zmh = ZmatecneHlasovani(stahni=stahni)
print(zmh.columns)
zmh.head()
2021-03-18:12:32:00 INFO     [utility.py:21] Stahuji 'https://www.psp.cz/eknih/cdrom/opendata/poslanci.zip'.
2021-03-18:12:32:02 INFO     [utility.py:21] Stahuji 'https://www.psp.cz/eknih/cdrom/opendata/hl-2017ps.zip'.
Index(['id_hlasovani', 'id_organ', 'schuze', 'cislo', 'bod', 'datum__ORIG',
       'cas', 'pro', 'proti', 'zdrzel', 'nehlasoval', 'prihlaseno', 'kvorum',
       'druh_hlasovani__ORIG', 'vysledek__ORIG', 'nazev_dlouhy',
       'nazev_kratky', 'datum', 'bod__KAT', 'vysledek', 'druh_hlasovani',
       'ma_zpochybneni', 'je_zmatecne', 'ma_stenozaznam', 'turn', 'typ__ORIG',
       'typ'],
      dtype='object')
Out[14]:
id_hlasovani id_organ schuze cislo bod datum__ORIG cas pro proti zdrzel ... datum bod__KAT vysledek druh_hlasovani ma_zpochybneni je_zmatecne ma_stenozaznam turn typ__ORIG typ
0 6018 <NA> <NA> <NA> <NA> <NA> NaN <NA> <NA> <NA> ... NaT <NA> <NA> <NA> NaN NaN NaN <NA> <NA> <NA>
1 7496 <NA> <NA> <NA> <NA> <NA> NaN <NA> <NA> <NA> ... NaT <NA> <NA> <NA> NaN NaN NaN <NA> <NA> <NA>
2 7506 <NA> <NA> <NA> <NA> <NA> NaN <NA> <NA> <NA> ... NaT <NA> <NA> <NA> NaN NaN NaN <NA> <NA> <NA>
3 7854 <NA> <NA> <NA> <NA> <NA> NaN <NA> <NA> <NA> ... NaT <NA> <NA> <NA> NaN NaN NaN <NA> <NA> <NA>
4 7934 <NA> <NA> <NA> <NA> <NA> NaN <NA> <NA> <NA> ... NaT <NA> <NA> <NA> NaN NaN NaN <NA> <NA> <NA>

5 rows × 27 columns

In [15]:
# načti indiviuální hlasování poslanců
hp = HlasovaniPoslanci(stahni=stahni)
print(hp.columns)
hp.head(2)
2021-03-18:12:32:05 INFO     [utility.py:21] Stahuji 'https://www.psp.cz/eknih/cdrom/opendata/poslanci.zip'.
2021-03-18:12:32:06 WARNING  [Snemovna.py:149] While merging 'funkce' with 'typ_funkce': Dropping ['nazev_typ_organ_cz__typ_funkce', 'id_typ_organ__typ_funkce', 'typ_id_typ_organ__typ_funkce', 'nazev_typ_organ_en__typ_funkce', 'typ_organ_obecny__typ_funkce'] because of abundance.
2021-03-18:12:32:07 INFO     [utility.py:21] Stahuji 'https://www.psp.cz/eknih/cdrom/opendata/hl-2017ps.zip'.
Index(['id_hlasovani', 'nazev_dlouhy', 'vysledek', 'id_poslanec', 'id_osoba',
       'pred', 'jmeno', 'prijmeni', 'id_klub', 'nazev_klub_cz', 'zkratka_klub',
       'narozeni', 'pohlavi', 'za', 'id_kraj', 'nazev_kraj_cz', 'zkratka_kraj',
       'id_kandidatka', 'nazev_kandidatka_cz', 'zkratka_kandidatka', 'schuze',
       'cislo', 'bod', 'cas', 'datum', 'bod__KAT', 'druh_hlasovani',
       'ma_zpochybneni', 'id_parlament', 'id_organ', 'od_klub', 'do_klub',
       'je_zmatecne'],
      dtype='object')
Out[15]:
id_hlasovani nazev_dlouhy vysledek id_poslanec id_osoba pred jmeno prijmeni id_klub nazev_klub_cz ... cas datum bod__KAT druh_hlasovani ma_zpochybneni id_parlament id_organ od_klub do_klub je_zmatecne
index
0 67018 Inf. o ustavení volební komise PS a volbě členů ano 1521 5700 Mgr. Ivan Adamec 1295 Poslanecký klub Občanské demokratické strany ... 13:53:00 2017-11-20 13:53:00+01:00 normální normální False 172 172 2017-10-24 00:00:00+02:00 NaT True
1 67018 Inf. o ustavení volební komise PS a volbě členů ano 1522 6254 prof. MUDr. Věra Adámková 1292 Poslanecký klub ANO 2011 ... 13:53:00 2017-11-20 13:53:00+01:00 normální normální False 172 172 2017-10-24 00:00:00+02:00 NaT True

2 rows × 33 columns

In [16]:
assert hp.id_osoba.nunique() <= p.id_osoba.nunique() or print("Nekonzistence v tabulkách 'HlasovaniPoslanci' a 'Poslanci': hlasujících poslanců je méně než těch, kteří hlasovali!")
In [17]:
# načti omluvy poslanců ze schůze PS
om = Omluvy(stahni=stahni)
print(om.columns)
om.head(2)
2021-03-18:12:32:31 INFO     [utility.py:21] Stahuji 'https://www.psp.cz/eknih/cdrom/opendata/poslanci.zip'.
2021-03-18:12:32:33 WARNING  [Snemovna.py:149] While merging 'funkce' with 'typ_funkce': Dropping ['nazev_typ_organ_cz__typ_funkce', 'id_typ_organ__typ_funkce', 'typ_id_typ_organ__typ_funkce', 'nazev_typ_organ_en__typ_funkce', 'typ_organ_obecny__typ_funkce'] because of abundance.
2021-03-18:12:32:33 INFO     [utility.py:21] Stahuji 'https://www.psp.cz/eknih/cdrom/opendata/hl-2017ps.zip'.
2021-03-18:12:32:38 WARNING  [Snemovna.py:149] While merging 'omluvy' with 'poslanci': Dropping ['id_organ__poslanci'] because of abundance.
Index(['id_organ', 'id_poslanec', 'den__ORIG', 'od__ORIG', 'do__ORIG', 'od',
       'do', 'den', 'id_osoba', 'id_kraj', 'id_kandidatka', 'web', 'ulice',
       'obec', 'psc', 'email', 'telefon', 'fax', 'psp_telefon', 'facebook',
       'foto', 'pred', 'prijmeni', 'jmeno', 'za', 'narozeni', 'pohlavi__ORIG',
       'zmena', 'umrti', 'pohlavi', 'adresa', 'sirka', 'delka',
       'nazev_kandidatka_cz', 'zkratka_kandidatka', 'nazev_kraj_cz',
       'zkratka_kraj', 'id_parlament', 'od_parlament', 'do_parlament',
       'id_klub', 'nazev_klub_cz', 'zkratka_klub', 'od_klub', 'do_klub',
       'je_poslanec'],
      dtype='object')
Out[17]:
id_organ id_poslanec den__ORIG od__ORIG do__ORIG od do den id_osoba id_kraj ... zkratka_kraj id_parlament od_parlament do_parlament id_klub nazev_klub_cz zkratka_klub od_klub do_klub je_poslanec
index
21451 172 1554 20.11.2017 <NA> <NA> NaN NaN 2017-11-20 00:00:00+01:00 5261 581 ... PH 172 2017-10-21 14:00:00+02:00 NaT 1293 Poslanecký klub Komunistické strany Čech a Moravy KSČM 2017-10-24 00:00:00+02:00 NaT True
21452 172 1533 22.11.2017 <NA> <NA> NaN NaN 2017-11-22 00:00:00+01:00 6002 590 ... V 172 2017-10-21 14:00:00+02:00 NaT 1294 Poslanecký klub České strany sociálně demokratické ČSSD 2017-10-27 00:00:00+02:00 NaT True

2 rows × 46 columns

In [18]:
assert om[om.je_poslanec].id_osoba.nunique() <= p.id_osoba.nunique() or print("Nekonzistence v tabulkách 'Omluvy' a 'Poslanci': osob je méně než osob, které se omlouvali!")
In [19]:
# Stenotexty zde nebudeme potřebovat
#st = Stenotexty(stahni=stahni)
#print(st.columns)
#st.head()

Analýza

In [20]:
volebni_obdobi = h.volebni_obdobi
snemovna = h.snemovna
In [21]:
pocet_poslancu_dle_klubu =  p[p.do_parlament.isna()].groupby('zkratka_klub').size().sort_values(ascending=False)

fig = go.Figure(go.Bar(
    x=pocet_poslancu_dle_klubu.index,
    y=pocet_poslancu_dle_klubu.values,
    marker=dict(
        color=list(range(len(pocet_poslancu_dle_klubu.index))),
        colorscale=categorical_scale1
    ),
    hovertemplate="<b>%{x}</b><br>Počet poslanců: %{y}<extra></extra>"
))

layout = go.Layout(
    title="Aktuální počet poslanců dle poslaneckého klubu (strany)",    
    xaxis=dict(title="Strana", type='category'),
    yaxis=dict(title="Počet poslanců")
)

fig.update_layout(clean_layout_with_y_spikes)
fig.update_layout(layout)

fig.show()

Kdy se konaly schůze PS?

In [22]:
fig = go.Figure()

schuze = sch[sch.pozvanka.isna()]
for _, s in schuze.iterrows():
    id_schuze = s.schuze
    dny_hlasovani = h[h.schuze == id_schuze].datum.dt.date.unique()
    od = s.od_schuze
    do = sch.tzn.localize(datetime.today()) if pd.isna(s.do_schuze) else s.do_schuze
    dny = [od.date()] + [d for d in dny_hlasovani if (d >= od.date()) and ((d <= do.date()) | pd.isna(do))] + [do.date()]
    dny = list(set(dny))
    datum_hovertemplate = f"od {od.strftime(format_den)}" if pd.isna(s.do_schuze) \
        else f"{od.strftime(format_den)} - {s.do_schuze.strftime(format_den)}"
    tm_line_hovertemplate= '' if pd.isna(s.tm_line) else s.tm_line
    fig.add_trace(go.Scatter(
        x=dny,
        y=[s.schuze]*len(dny),
        text=s.schuze,
        hovertemplate=f"Schůze {s.schuze}<br>" \
            "Datum: %{x}<br>"\
            f"Trvání schůze: {datum_hovertemplate}<br>"\
            f"Typ schůze: {s.typ}<br>" \
            f"{tm_line_hovertemplate}<extra></extra>",
        mode="lines+markers",
        line = dict(shape='linear', width=15),
        marker = dict(symbol='star-diamond', size=10),
     ))
fig.update_traces(marker=dict(line=dict(width=1, color='black'))) 

fig.update_layout(clean_layout_with_xy_spikes)
layout = go.Layout(
    title="Schůze poslanecké sněmovny v čase",    
    xaxis=dict(title="Čas"),
    yaxis=dict(title="Schůze", type='category'),
    height=600,
    showlegend=False
)
fig.update_layout(layout)


fig.show()

Počty hlasování

In [23]:
import plotly.express as px
h["pocet_dni_na_schuzi"] = h.groupby(["schuze"]).datum.transform('nunique')

h['den'] = h.datum.dt.date
h["den_schuze"] = h.groupby(["schuze", h.datum.dt.date]).ngroup()
h["den_schuze_min"] = h.groupby(["schuze"]).den_schuze.transform(min)
h["den_schuze_rank"] = (h["den_schuze"] - h["den_schuze_min"] + 1)

x1 = h.groupby(["schuze", 'den', "den_schuze_rank"]).size()
z1 = x1.reset_index(name="pocet_hlasovani")

print(f"Na schůzích {sorted(set(range(z1.schuze.max() + 1)) - set(z1.schuze.unique()) - set([0]))} se nehlasovalo.")
fig = px.bar(z1, x="schuze", y="pocet_hlasovani",
            color="den_schuze_rank",
            hover_data=['schuze', 'den_schuze_rank', 'pocet_hlasovani', 'den'],
            labels={'schuze':'Schůze', 'den': 'Datum', 'den_schuze_rank': 'Pořadí dne schůze', 'pocet_hlasovani': 'Počet hlasování'},
            title="Počet hlasování dle dne schůze")

layout = go.Layout(
    title="Počet hlasování dle schůze",    
    plot_bgcolor="#FFFFFF",
    #hovermode="x",
    #hoverdistance=100, # Distance to show hover label of data point
    #spikedistance=1000, # Distance to show spike
    xaxis=dict(
        title="Schůze sněmovny",
        linecolor="#BCCCDC",
        #type='category'
    ),
    yaxis=dict(
        title="Počet hlasování",
        linecolor="#BCCCDC",
        showspikes=True,
        spikethickness=1,
        spikedash="dot",
        spikecolor="#999999",
        spikemode="across",
    )
)

fig.update_layout(layout)
Na schůzích [10, 21, 71] se nehlasovalo.

Hlasování dle data

In [24]:
def pocet_hlasovani_dle_data(df, resample_to, resample_str, resample_label):
    frame = df.set_index('datum').resample(resample_to).size()
    frame = frame.mask(frame == 0, None).dropna()

    max_idx = frame.sort_values().index[-1]
    min_idx = frame.sort_values().index[0]
    print(f"Nejvíce hlasování ({frame.loc[max_idx]}) se uskutečnilo {max_idx.strftime(resample_str)}.")
    print(f"Nejméně hlasování ({frame.loc[min_idx]}) se uskutečnilo {min_idx.strftime(resample_str)}.")

    fig = go.Figure()
    fig.add_trace(go.Bar(
        x=frame.index,
        y=frame.values,
        marker=dict(
            color=frame.values,
            colorscale='Bluered'
        ),

        hovertemplate="%{x}<br>počet hlasování: %{y}<extra></extra>"
    ))
    
    fig.update_layout(
        title=f"Počet hlasování dle data ({resample_label})",
        xaxis_title=f"datum ({resample_label})",
        yaxis_title="počet hlasování",
        width=1200,
        height=500
    )

    dt_all = pd.date_range(start=frame.index[0],end=frame.index[-1])
    dt_obs = [d.strftime(resample_str) for d in frame.index]
    dt_breaks = [d for d in dt_all.strftime(resample_str).tolist() if not d in dt_obs]
    # nezobrazuj data bez hlasování
    #dt_all = pd.date_range(start=df.index[0], end=df.index[-1])
    #dt_obs = [d.strftime(resample_str) for d in df.index]
    #dt_breaks = [d for d in dt_all.strftime(resample_str).tolist() if not d in dt_obs]

    fig.update_xaxes(
        rangebreaks=[dict(values=dt_breaks)]
    )
    
    fig.show()
pocet_hlasovani_dle_data(h, "D", "%Y-%m-%d", "den")
Nejvíce hlasování (387) se uskutečnilo 2019-06-05.
Nejméně hlasování (1) se uskutečnilo 2018-07-12.

Omluvy poslanců z hlasování

In [25]:
# Za hlasovací dny označujeme takové, během nichž se sešla Sněmovna a o něčem se hlasovalo.
# TODO: Existují dny, kdy se konala schůze PS, ale o ničem se nehlasovalo? Jak je najdeme?
minimalni_pocet_hlasovacich_dni = 30

hlasovaci_dny = pd.to_datetime(h.datum.dt.date.unique()).tz_localize(h.tzn)
pocet_hlasovacich_dni = len(hlasovaci_dny)
In [26]:
def fce_pocet_omluvenych_dni(id_poslanec):
    return om[(om.je_poslanec) & (om.id_poslanec == id_poslanec)].den.dt.date.nunique()

p['pocet_omluvenych_dni'] = p.id_poslanec.apply(fce_pocet_omluvenych_dni).astype('Int64')
assert p[p['pocet_omluvenych_dni'] > pocet_hlasovacich_dni].index.size == 0, \
    f"Chyba ve výpočtu. Počet omluvenych dní poslance musí být menší než {pocet_hlasovacich_dni}."

p['pocet_hlasovacich_dni'] = p.apply(lambda row: 
        len(hlasovaci_dny[(hlasovaci_dny >= row['od_parlament'])
        & ((hlasovaci_dny <= row['do_parlament']) | pd.isna(row['do_parlament']))])
        , axis=1).astype('Int64')
assert p[p['pocet_hlasovacich_dni'] > pocet_hlasovacich_dni].index.size == 0,\
    f"Chyba ve výpočtu. Počet hlasovacích dní poslance musí být menší než {pocet_hlasovacich_dni}."

p['pomer_omluvenych_dni'] = p['pocet_omluvenych_dni'] / p['pocet_hlasovacich_dni']
assert p[
    (p.pocet_hlasovacich_dni > minimalni_pocet_hlasovacich_dni)
    & ((p['pomer_omluvenych_dni'] > 1) | (p['pomer_omluvenych_dni'] < 0))].index.size == 0,\
    f"Chyba ve výpočtu. Poměr omluvených dní poslance musí být v intervalu [0, 1]]."
In [27]:
if pd.isna(h.snemovna.do_organ):
    print(f"Sněmovna {volebni_obdobi} se začala scházet {snemovna.od_organ.strftime(format_den)}.")
    if len(h) > 0:
        print(f"Naposledy se hlasovalo {h.datum.sort_values().dt.strftime(format_den).iloc[-1]}.")
else:
    print(f"Sněmovna {volebni_obdobi} se začala scházet {snemovna.od_organ.strftime(format_den)} a skončila {snemovna.do_organ.strftime(format_den)}.")

print(f"Hlasovalo se během {pocet_hlasovacich_dni} dní.")
Sněmovna 2017 se začala scházet 21. 10. 2017.
Naposledy se hlasovalo 12. 03. 2021.
Hlasovalo se během 271 dní.
In [28]:
print(f"Průměrný poměr omluvených dnů: {p[p.pocet_hlasovacich_dni >= minimalni_pocet_hlasovacich_dni].pomer_omluvenych_dni.mean():.2f}.")
print(f"Medián poměr omluvených dnů: {p[p.pocet_hlasovacich_dni >= minimalni_pocet_hlasovacich_dni].pomer_omluvenych_dni.median():.2f}.")

fig = go.Figure(go.Box(y=p[p.pocet_hlasovacich_dni >= minimalni_pocet_hlasovacich_dni].pomer_omluvenych_dni))
fig.show()
Průměrný poměr omluvených dnů: 0.40.
Medián poměr omluvených dnů: 0.37.
In [29]:
fig = go.Figure()

for zkratka_klub in p.zkratka_klub.unique():
    data = p[(p.zkratka_klub == zkratka_klub) & (p.pocet_hlasovacich_dni >= minimalni_pocet_hlasovacich_dni)]
    fig.add_trace(go.Box(y=data.pomer_omluvenych_dni, name=zkratka_klub))

layout = go.Layout(
    title="Poměr počtu omluvených dní ku počtu hlasovacích dní dle poslaneckého klubu (strany)",    
    xaxis=dict(title="Strany", type='category'),
    yaxis=dict(title="Poměr omluvených dní")
)

fig.update_layout(clean_layout_with_x_spikes)
fig.update_layout(layout)

fig.show()
In [30]:
data = pd.DataFrame([])
for zkratka_klub in p.zkratka_klub.unique():
    v = p[(p.zkratka_klub == zkratka_klub) & (p.pocet_hlasovacich_dni >= minimalni_pocet_hlasovacich_dni)].pocet_omluvenych_dni.sum()
    data.loc[zkratka_klub, 'pocet_omluvenych_dni'] = v
    
fig = go.Figure()

fig.add_trace(go.Bar(x=data.index, y=data.pocet_omluvenych_dni))
    
layout = go.Layout(
    title="Počet omluvených dní dle poslaneckého klubu (strany)",    
    xaxis=dict(title="Strany", type='category'),
    yaxis=dict(title="Poměr omluvených dní")
)

fig.update_layout(clean_layout_with_x_spikes)
fig.update_layout(layout)

fig.show()
In [31]:
print(f"Průměrný počet omluvených dnů: {p[(p.pocet_hlasovacich_dni >= minimalni_pocet_hlasovacich_dni)].pocet_omluvenych_dni.mean():.1f}.")
print(f"Medián počtu omluvených dnů: {p[(p.pocet_hlasovacich_dni >= minimalni_pocet_hlasovacich_dni)].pocet_omluvenych_dni.median():.1f}.")
Průměrný počet omluvených dnů: 100.7.
Medián počtu omluvených dnů: 97.0.
In [32]:
go.Figure(go.Histogram(x=p[(p.pocet_hlasovacich_dni >= minimalni_pocet_hlasovacich_dni)].pomer_omluvenych_dni))
In [33]:
go.Figure(go.Histogram(x=p[(p.pocet_hlasovacich_dni >= minimalni_pocet_hlasovacich_dni)].pocet_omluvenych_dni))
In [34]:
cnt = 10
print(f"Poslanci s nejmenším počtem dnů, během nichž se omluvali z hlasování PS.")
fields = ['id_poslanec', 'jmeno', 'prijmeni', 'pomer_omluvenych_dni', 'pocet_omluvenych_dni', 'pocet_hlasovacich_dni', 'zkratka_klub']
p[(p.pocet_hlasovacich_dni >= minimalni_pocet_hlasovacich_dni)].sort_values(by="pocet_omluvenych_dni")[fields].head(cnt)
Poslanci s nejmenším počtem dnů, během nichž se omluvali z hlasování PS.
Out[34]:
id_poslanec jmeno prijmeni pomer_omluvenych_dni pocet_omluvenych_dni pocet_hlasovacich_dni zkratka_klub
index
86 1608 Vladimír Koníček 0.051282 4 78 KSČM
132 1654 Robert Pelikán 0.297872 14 47 ANO
191 1713 Veronika Vrecionová 0.116279 15 129 ODS
131 1653 Mikuláš Peksa 0.172131 21 122 Piráti
213 1735 Jaroslav Vymazal 0.897959 44 49 ODS
164 1686 Martin Stropnický 0.714286 45 63 ANO
212 1734 Irena Blažková 0.881356 52 59 ANO
190 1712 Petr Vrána 0.259434 55 212 ANO
64 1586 Milan Chovanec 0.556604 59 106 ČSSD
32 1554 Jiří Dolejš 0.221402 60 271 KSČM
In [35]:
cnt = 10
print(f"Poslanci s největším počtem dnů, během nichž se omluvali z hlasování PS.")
fields = ['id_poslanec', 'jmeno', 'prijmeni', 'pomer_omluvenych_dni', 'pocet_omluvenych_dni', 'pocet_hlasovacich_dni', 'zkratka_klub']
p[(p.pocet_hlasovacich_dni >= minimalni_pocet_hlasovacich_dni)].sort_values(by="pocet_omluvenych_dni")[::-1][fields].head(cnt)
Poslanci s největším počtem dnů, během nichž se omluvali z hlasování PS.
Out[35]:
id_poslanec jmeno prijmeni pomer_omluvenych_dni pocet_omluvenych_dni pocet_hlasovacich_dni zkratka_klub
index
3 1524 Andrej Babiš 0.804428 218 271 ANO
161 1683 Antonín Staněk 0.730627 198 271 ČSSD
12 1533 Jiří Běhounek 0.715867 194 271 ČSSD
158 1680 Karel Schwarzenberg 0.715867 194 271 TOP09
101 1623 Jana Levová 0.704797 191 271 SPD
81 1603 Jiří Kobza 0.619926 168 271 SPD
19 1541 Jan Birke 0.608856 165 271 ČSSD
170 1692 Julius Špičák 0.605166 164 271 ANO
56 1578 Jan Hamáček 0.605166 164 271 ČSSD
63 1585 Tereza Hyťhová 0.583026 158 271 Nezařaz
In [36]:
cnt = 10
print(f"Poslanci s nejmenším poměrem dnů, během nichž se omluvali z hlasování PS.")
fields = ['id_poslanec', 'jmeno', 'prijmeni', 'pomer_omluvenych_dni', 'pocet_omluvenych_dni', 'pocet_hlasovacich_dni', 'zkratka_klub']
p[(p.pocet_hlasovacich_dni >= minimalni_pocet_hlasovacich_dni)].sort_values(by="pomer_omluvenych_dni")[fields].head(cnt)
Poslanci s nejmenším poměrem dnů, během nichž se omluvali z hlasování PS.
Out[36]:
id_poslanec jmeno prijmeni pomer_omluvenych_dni pocet_omluvenych_dni pocet_hlasovacich_dni zkratka_klub
index
86 1608 Vladimír Koníček 0.051282 4 78 KSČM
191 1713 Veronika Vrecionová 0.116279 15 129 ODS
131 1653 Mikuláš Peksa 0.172131 21 122 Piráti
32 1554 Jiří Dolejš 0.221402 60 271 KSČM
107 1629 Tomáš Martínek 0.225092 61 271 Piráti
41 1563 Milan Feranec 0.239852 65 271 ANO
97 1619 Roman Kubíček 0.243542 66 271 ANO
7 1528 Lukáš Bartoň 0.243542 66 271 Piráti
18 1540 Stanislav Berkovec 0.243542 66 271 ANO
157 1679 Jan Schiller 0.254032 63 248 ANO
In [37]:
cnt = 10
print(f"Poslanci s největším poměrem dnů, během nichž se omluvali z hlasování PS.")
fields = ['id_poslanec', 'jmeno', 'prijmeni', 'pomer_omluvenych_dni', 'pocet_omluvenych_dni', 'pocet_hlasovacich_dni', 'zkratka_klub']
p[(p.pocet_hlasovacich_dni >= minimalni_pocet_hlasovacich_dni)].sort_values(by="pomer_omluvenych_dni")[::-1][fields].head(cnt)
Poslanci s největším poměrem dnů, během nichž se omluvali z hlasování PS.
Out[37]:
id_poslanec jmeno prijmeni pomer_omluvenych_dni pocet_omluvenych_dni pocet_hlasovacich_dni zkratka_klub
index
213 1735 Jaroslav Vymazal 0.897959 44 49 ODS
212 1734 Irena Blažková 0.881356 52 59 ANO
3 1524 Andrej Babiš 0.804428 218 271 ANO
161 1683 Antonín Staněk 0.730627 198 271 ČSSD
12 1533 Jiří Běhounek 0.715867 194 271 ČSSD
158 1680 Karel Schwarzenberg 0.715867 194 271 TOP09
164 1686 Martin Stropnický 0.714286 45 63 ANO
101 1623 Jana Levová 0.704797 191 271 SPD
207 1729 Václav Votava 0.660606 109 165 ČSSD
172 1694 Dan Ťok 0.622642 66 106 ANO

Jak se hlasuje?

Následující analýza slouží k tomu, abychom lépe pochopili, jakým způsobem se hlasujev PS ČR. Proč je tomu třeba rozumět? Některá hlasování (například hlasování o zpochybnění hlasování) mají technický charakter. V některých analýzách je může být vhodné vynechat.

Platnost hlasování

Přibližná pravidla pro určení platnosti hlasování:

  • Hlasování může být označeno jako zmatečné. Zmatečné hlasování je neplatné.
  • Hlasování může být zpochybněno.
    • O zpochybnění se může hlasovat.
      • Pokud se o zpochybnění hlasování hlasuje a zpochybnění je přijato, dochází k opakovanému hlasování. Původní hlasování je neplatné.
        • V tabulce ZpochybněníHlasování je uveden identifikátor hlasování o zpochybnění hlasování (id_h2) a identifikátor opakovaného hlasování (id_h3).
        • Hlasování o zpochybnění hlasování lze zpochybnit.
        • Identifikátor opakovaného hlasování označuje první platné hlasování v řadě hlasování.
      • Pokud se o zpochybnění hlasování nehlasuje nebo pokud není zpochybnění hlasování přijato, je v tabulce ZpochybneniHlasování nastaveno 'pouze pro stenozáznam'. Původní hlasování zůstává platné.
In [38]:
hl_pocet = h.id_hlasovani.nunique()
zpochybneni = zph[zph.je_platne == True]
zm_hl = h[h.id_hlasovani.isin(zmh.id_hlasovani)]
zm_hl_pocet = zm_hl.id_hlasovani.nunique()
zp_hl = h[h.id_hlasovani.isin(zpochybneni.id_hlasovani)]
zp_hl_pocet = zp_hl.id_hlasovani.nunique()

pouze_v_tabulce_zpochybneni_pocet = len(set(zph.id_hlasovani) - set(h.id_hlasovani))
pouze_v_tabulce_zmatecne_pocet = len(set(zmh.id_hlasovani) - set(h.id_hlasovani))

print(f"Základní vlastnosti tabulky Hlasovani (pro sněmovnu  {h.volebni_obdobi}):")
print(f"- {hl_pocet} hlasování, z toho")
print(f"- {zm_hl_pocet} ({100 * (zm_hl_pocet / hl_pocet):.2f}%) hlasování bylo označeno za zmatečné,")
print(f"- {zp_hl_pocet} ({100 * (zp_hl_pocet / hl_pocet):.2f}%) hlasování bylo zpochybněno.")

df = zpochybneni[zpochybneni.mode__KAT == 'pouze pro stenozáznam']
v1 = h[h.id_hlasovani.isin(df.id_hlasovani)].id_hlasovani.nunique()
print(f"\t- {v1} hlasování z tabulky Hlasování bylo zpochybněno, ale zpochybnění bylo uvedeno jen pro stenozáznam.")

df = zpochybneni[zpochybneni.mode__KAT == 'žádost o opakování']
v1 = h[h.id_hlasovani.isin(df.id_hlasovani)].id_hlasovani.nunique()
print(f"\t- {v1} hlasování z tabulky Hlasování bylo zpochybněno s žádostí o opakování hlasování.")

df = zpochybneni[(zpochybneni.mode__KAT == 'žádost o opakování') & ~(zpochybneni.id_h2.isna()| zpochybneni.id_h3.isna())]
v1 = h[h.id_hlasovani.isin(df.id_hlasovani)].id_hlasovani.nunique()
print(f"\t\t- {v1} hlasování z tabulky Hlasování bylo zpochybněno s žádostí o opakování hlasování, o zpochybnění se hlasovalo a původní hlasování se následně opakovalo.")

df = zpochybneni[(zpochybneni.mode__KAT == 'žádost o opakování') & ~zpochybneni.id_h2.isna() & zpochybneni.id_h3.isna()]
v1 = h[h.id_hlasovani.isin(df.id_hlasovani)].id_hlasovani.nunique()
print(f"\t\t- {v1} hlasování z tabulky Hlasování bylo zpochybněno s žádostí o opakování hlasování, o zpochybnění se hlasovalo, ale původní hlasování se neopakovalo.")

print()
print(f"Dalších {pouze_v_tabulce_zpochybneni_pocet} hlasování je uvedeno pouze v tabulce ZpochybneniHlasovani. ")
print(f"Dalších {pouze_v_tabulce_zmatecne_pocet} hlasování je uvedeno pouze v tabulce ZmatecneHlasovani.")
print()
print(f"Celkově tedy proběhlo ve sněmovně až {hl_pocet + pouze_v_tabulce_zpochybneni_pocet + pouze_v_tabulce_zmatecne_pocet} hlasování.")
print()
print("Poznámka: Tabulka ZmatecneHlasovani nemá explicitně určenou vazbu na danou sněmovnu. Její propojení s tabulkou Hlasování bylo provedeno následující heuristikou: "
      "Pro danou sněmovnu se určí minimální a maximální identifikátor hlasování. Pro dané zmatečné hlasování pak musí platit, že min(id(hlasování)) <= id(zmatečného hlasování) <= max(id(hlasování)).")
Základní vlastnosti tabulky Hlasovani (pro sněmovnu  2017):
- 8700 hlasování, z toho
- 111 (1.28%) hlasování bylo označeno za zmatečné,
- 209 (2.40%) hlasování bylo zpochybněno.
	- 136 hlasování z tabulky Hlasování bylo zpochybněno, ale zpochybnění bylo uvedeno jen pro stenozáznam.
	- 73 hlasování z tabulky Hlasování bylo zpochybněno s žádostí o opakování hlasování.
		- 72 hlasování z tabulky Hlasování bylo zpochybněno s žádostí o opakování hlasování, o zpochybnění se hlasovalo a původní hlasování se následně opakovalo.
		- 1 hlasování z tabulky Hlasování bylo zpochybněno s žádostí o opakování hlasování, o zpochybnění se hlasovalo, ale původní hlasování se neopakovalo.

Dalších 512 hlasování je uvedeno pouze v tabulce ZpochybneniHlasovani. 
Dalších 684 hlasování je uvedeno pouze v tabulce ZmatecneHlasovani.

Celkově tedy proběhlo ve sněmovně až 9896 hlasování.

Poznámka: Tabulka ZmatecneHlasovani nemá explicitně určenou vazbu na danou sněmovnu. Její propojení s tabulkou Hlasování bylo provedeno následující heuristikou: Pro danou sněmovnu se určí minimální a maximální identifikátor hlasování. Pro dané zmatečné hlasování pak musí platit, že min(id(hlasování)) <= id(zmatečného hlasování) <= max(id(hlasování)).
In [39]:
def flatten(ary):
    return [x for l in ary for x in l]

def fce_mezi_hlasovanim_a_hlasovanim_o_zpochybneni_hlasovani_ids(row):
    if pd.isna(row['id_h2']):
        return []
    else:
        return list(range(row['id_hlasovani']+1, row['id_h2']))

# Hlasování o zpochybnění hlasování je možné také zkazit nebo zpochybnit.
# Mezi prvním hlasováním o zpochybnění a opakovaným hlasováním může proběhnout několik dalších zpochybněných nebo neplatných hlasování.
def fce_mezi_hlasovanim_o_zpochybneni_a_opakovanym_hlasovanim_ids(row):
    if pd.isna(row['id_h2']):
        return []
    elif pd.isna(row['id_h3']):
        return []
    else:
        return list(range(row['id_h2']+1, row['id_h3']))

mezi_hlasovanim_a_hlasovanim_o_zpochybneni_hlasovani_ids = \
    flatten(zpochybneni[zpochybneni.mode__KAT == 'žádost o opakování'].apply(fce_mezi_hlasovanim_a_hlasovanim_o_zpochybneni_hlasovani_ids, axis=1))

mezi_hlasovanim_o_zpochybneni_a_opakovanym_hlasovani_ids = \
    flatten(zpochybneni[zpochybneni.mode__KAT == 'žádost o opakování'].apply(fce_mezi_hlasovanim_o_zpochybneni_a_opakovanym_hlasovanim_ids, axis=1))

hlasovani_o_zpochybneni_ids = h[h.id_hlasovani.isin(zpochybneni.id_h2.unique())]

hlasovani_bez_zmatecnych_a_zpochybnenych = h[~h.id_hlasovani.isin(zmh.id_hlasovani)
                     & ~h.id_hlasovani.isin(zph[zph.mode__KAT == 'žádost o opakování'].id_hlasovani)
                     & ~h.id_hlasovani.isin(mezi_hlasovanim_o_zpochybneni_a_opakovanym_hlasovani_ids)
                     & ~h.id_hlasovani.isin(hlasovani_o_zpochybneni_ids)
]

Míra jednomyslnosti hlasování

In [40]:
h_platne = hlasovani_bez_zmatecnych_a_zpochybnenych.copy()
In [41]:
h_platne["mira_jednomyslnosti"] = (h_platne.pro - h_platne.proti).abs() / (h_platne.pro + h_platne.proti)
h_platne["mira_jednomyslnosti"].describe()

fig = go.Figure(go.Histogram(
    x=h_platne.mira_jednomyslnosti,
    hovertemplate="míra jednomyslnosti: %{x}<br>počet hlasování: %{y}<extra></extra>"))
fig.update_layout(width=700, height=400, xaxis=dict(title="míra jednomyslnosti"), yaxis=dict(title="počet hlasování"))
fig.show()
In [42]:
# denní míra jednomyslnosti

precision = 3 

def A_razeno_dle_B(x, A, B):
    df = x.groupby(A)[B].mean().sort_values(ascending=True)
    return("<br>".join(map(str, df.index.astype(str) + ': ' + df.values.round(precision).astype(str))))

days = 7
interval = f"{days}D"

df = pd.DataFrame()
df['mira_jednomyslnosti_za_den_mean'] = h_platne.groupby(h_platne.datum.dt.date).mira_jednomyslnosti.mean()
df['nazev_dlouhy_za_den'] = h_platne.groupby(h_platne.datum.dt.date)[['nazev_dlouhy', 'mira_jednomyslnosti']].apply(
    lambda x: A_razeno_dle_B(x, "nazev_dlouhy", "mira_jednomyslnosti")
) 
df.index = pd.to_datetime(df.index)

fig = go.Figure()

fig.add_trace(go.Scatter(
    x=df.index,
    y=df.mira_jednomyslnosti_za_den_mean,
    text=df.nazev_dlouhy_za_den,
    name="míra jednomyslnosti za den",
    mode="markers",
     marker=dict(
        size=5,
        color=df.mira_jednomyslnosti_za_den_mean,
        colorscale='RdYlGn', # one of plotly colorscales
        showscale=True
    ),
    hovertemplate =
        '<b>%{x}</b>'+
        '<br><br><b>Průměrná míra jednomyslnosti</b>: <i>%{y:.3f}</i>'+
        '<br><br><b>Hlasování dle míry jednomyslnosti (vzestupně):</b><br>%{text}<extra></extra>',
    hoverlabel=dict(
        bgcolor='white',
        font=dict(color='black')
    )
))

fig.update_layout(title="Denní míra jednomyslnosti", xaxis=dict(title="datum [jednotlivé dny]"), yaxis=dict(title="míra jednomyslnosti"))

fig.show()
In [43]:
print(f"Jednomyslně schválená hlasování (vysoká míra jednomyslnosti): {h_platne[h_platne.mira_jednomyslnosti == 1].index.size}")
jednomyslna_temata = h_platne[h_platne.mira_jednomyslnosti == 1].groupby('nazev_dlouhy').size().sort_values(ascending=False)
print(jednomyslna_temata.head(20))
Jednomyslně schválená hlasování (vysoká míra jednomyslnosti): 3007
nazev_dlouhy
Pořad schůze                                                              518
                                                                          121
Návrh na vyslovení souhlasu PS s prodloužením doby nouzového stavu         68
Návrh na změny ve složení orgánů Poslanecké sněmovny                       59
Návrhy PS na propůjčení nebo udělení státních vyznamenání                  44
Vl.n.z. v souv.s elektronizací postupů orgánů veřejné moci                 33
Novela z. - školský zákon                                                  26
Návrhy Poslanecké sněmovny na propůjčení nebo udělení státních vyzname     24
Novela z. - trestní řád                                                    23
Novela z. o daních z příjmů                                                23
Novela z. o kompenzačním bonusu                                            23
Novela z. o státním rozpočtu ČR na rok 2020                                22
Vl. n. z. kterým se mění některé zákony v oblasti daní - EU                20
Vl.n.z. o evidenci skutečných majitelů - EU                                18
Usn.PS.1162                                                                18
Novela z. - trestní zákoník - EU                                           17
Novela ústav. z. - Ústava ČR                                               16
Náv. na ustavení výborů PS                                                 16
Novela z. o urychlení výstavby dopravní infrastruktury                     16
Novela z. o pomoci v hmotné nouzi                                          16
dtype: int64
In [44]:
print(f"Hlasování s nízkou mírou jednomyslnosti (zcela vyrovnané pro a proti)): {h_platne[h_platne.mira_jednomyslnosti == 0].index.size}")
nejednomyslna_temata = h_platne[h_platne.mira_jednomyslnosti == 0].groupby('nazev_dlouhy').size().sort_values(ascending=False)
print(nejednomyslna_temata.head(20))
Hlasování s nízkou mírou jednomyslnosti (zcela vyrovnané pro a proti)): 71
nazev_dlouhy
Novela z. o evidenci tržeb - EU                                 28
Pořad schůze                                                     7
Vl.n.z. o kompenzačním bonusu v souv. s krizovými opatřeními     5
Vl.n.z.o opatř.ke zmírnění dopadů SARS CoV-2 na nájemce byt.     2
Návrh na vyslovení nedůvěry vládě České republiky                2
Novela z. o rozpočtových pravidlech                              2
Vl.n.z. o státním rozpočtu ČR na rok 2021                        2
Novela z. - horní zákon                                          2
Novela z. o důchodovém pojištění                                 1
Novela z. o evidenci tržeb                                       1
Novela z. o státním občanství ČR                                 1
Novela z. o některých úpravách v oblasti evidence tržeb          1
Novela z. o bankách - EU                                         1
Novela z. o pomoci v hmotné nouzi                                1
N.z., kterým se mění zák. opatření Senátu č. 340/2013 Sb.        1
Novela z. o potravinách a tabákových výrobcích - EU              1
Vln. z. o kompenzačním bonusu pro rok 2021                       1
Novela z. o ČNB                                                  1
Novela z. o vnitrozemské plavbě - EU                             1
Novela z. o vodách (vodní zákon) - EU                            1
dtype: int64
In [45]:
# Témata hlasování s nejvyšší mírou jednomyslnosti a jejich počty

cnt = 5
x = pd.DataFrame()
x['celkovy_pocet_hlasovani_dle_tematu'] =  h_platne.groupby('nazev_dlouhy').size()
x['prumerna_mira_jednomyslnosti_dle_tematu'] = h_platne.groupby('nazev_dlouhy')['mira_jednomyslnosti'].mean()
x.sort_values(by='prumerna_mira_jednomyslnosti_dle_tematu', ascending=False).head(cnt)
Out[45]:
celkovy_pocet_hlasovani_dle_tematu prumerna_mira_jednomyslnosti_dle_tematu
nazev_dlouhy
Výroční zpráva o činnosti České televize v roce 2018 1 1.0
Sml. mezi ČR a Běloruskem o důchodovém zabezpečení 2 1.0
Výroční zpráva a účetní závěrka SFRB za rok 2016 1 1.0
Sml.mezi ČR a vl.Rep.Kazachstán o spol.v boji proti tr.činn. 2 1.0
Návrh Pravidel hospodaření poslaneckých klubů pro rok 2018 1 1.0
In [46]:
# Témata hlasování s nízkou mírou jednomyslnosti a jejich počty

cnt = 5
x = pd.DataFrame()
x['celkovy_pocet_hlasovani_dle_tematu'] =  h_platne.groupby('nazev_dlouhy').size()
x['prumerna_mira_jednomyslnosti_dle_tematu'] = h_platne.groupby('nazev_dlouhy')['mira_jednomyslnosti'].mean()
x.sort_values(by='prumerna_mira_jednomyslnosti_dle_tematu', ascending=False).tail(cnt)
Out[46]:
celkovy_pocet_hlasovani_dle_tematu prumerna_mira_jednomyslnosti_dle_tematu
nazev_dlouhy
Písemná interpelace V. Munzara na A. Schillerovou 1 0.083333
Novela z. o evidenci tržeb - EU 857 0.060626
Písemná interpelace V. Munzara na A. Schillerovou ve věci poruš. ústav 1 0.054054
Písemná interpelace V. Munzara na A. Schillerovou ve věci odměňování ú 1 0.038961
Návrh na vyslovení nedůvěry vládě České republiky 3 0.022556

Podobnost hlasování poslanců

In [47]:
%%time

df = hp[hp.id_hlasovani.isin(h_platne.id_hlasovani)]
data = df[['id_osoba', 'id_hlasovani', 'vysledek', 'zkratka_klub']]\
    .set_index('id_osoba')\
    .sort_values(by=['zkratka_klub'])[['id_hlasovani', 'vysledek']]\
    .groupby('id_osoba', sort=False)\
    .apply(lambda g: list(map(tuple, g.values))).to_dict()

data = {osoba: [str(hl_idx) + '_' + vysledek for hl_idx, vysledek in data[osoba]] for osoba in data.keys()}
CPU times: user 5.62 s, sys: 296 ms, total: 5.91 s
Wall time: 5.91 s
In [48]:
%%time

def jaccard_similarity(list1, list2):
    intersection = len(list(set(list1).intersection(list2)))
    union = (len(list1) + len(list2)) - intersection
    return float(intersection) / union

m = {}
for osoba1, v1 in data.items():
    if osoba1 not in m:
        m[osoba1] = {}
    for osoba2, v2 in data.items():
        m[osoba1][osoba2] = jaccard_similarity(v1, v2)
CPU times: user 48.8 s, sys: 13 ms, total: 48.9 s
Wall time: 48.9 s
In [49]:
korelace_df = pd.DataFrame(m)
osoby = {id_osoba: hp[hp.id_osoba == id_osoba][['jmeno', 'prijmeni', 'zkratka_kandidatka','nazev_kraj_cz', 'zkratka_klub']].iloc[0] for id_osoba in korelace_df}
jmeno_prijmeni = [f"{osoby[id_osoba]['jmeno']} {osoby[id_osoba]['prijmeni']}" for id_osoba in korelace_df.index]
zkratka_klub = [osoby[id_osoba]['zkratka_klub'] for id_osoba in korelace_df.index]
label = [f"{j} ({zk})" for j, zk in zip(jmeno_prijmeni, zkratka_klub)]
In [50]:
import plotly.graph_objects as go
fig = go.Figure(data=go.Heatmap(
        z=korelace_df,
        x=label,
        y=label,
        colorscale='Viridis',
        hovertemplate="%{x}<br>%{y}<br>Podobnost hlasování: %{z}<extra></extra>"
))

fig.update_layout(title='Podobnost hlasování poslanců',  width=800, height=800)
fig.update_xaxes(type='category', tickangle=45)
fig.update_yaxes(type='category', autorange='reversed')
fig.show()
In [51]:
print(f"Poslední běh notebooku: {datetime.now().strftime('%d.%m.%Y %H:%M:%S')}.")
Poslední běh notebooku: 18.03.2021 12:33:43.
In [ ]: